﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using StyleMVVM.Data.Collections;
using StyleMVVM.Logging;
using StyleMVVM.Utilities;

namespace StyleMVVM.Data
{
	public class WeakNotifyObject : LogAwareObject, INotifyPropertyChanged
	{
		private readonly object lockObject = new object();
		private readonly List<GenericWeakAction<object, PropertyChangedEventArgs>> propertychangeHandlers;

		public WeakNotifyObject()
		{
			propertychangeHandlers = new List<GenericWeakAction<object, PropertyChangedEventArgs>>();
		}

		public event PropertyChangedEventHandler PropertyChanged
		{
			add
			{
				lock (lockObject)
				{
					propertychangeHandlers.Add(new GenericWeakAction<object, PropertyChangedEventArgs>(value, false));
				}
			}
			remove
			{
				lock (lockObject)
				{
					foreach (GenericWeakAction<object, PropertyChangedEventArgs> propertychangeHandler in propertychangeHandlers)
					{
						// I know this might not be the one but since there is no method info any more to compare 
						// I'm not going to waste the time in an event handler remove to do the dynamic trick
						if (propertychangeHandler.Target == value.Target)
						{
							propertychangeHandlers.Remove(propertychangeHandler);
							break;
						}
					}
				}
			}
		}

		/// <summary>
		/// Sets a value to a particular property while calling PropertyChanged
		/// Note: Default implementation from VS temlate
		/// </summary>
		/// <typeparam name="T">type of property</typeparam>
		/// <param name="storage">backing field for the property</param>
		/// <param name="value">new value to set</param>
		/// <param name="propertyName">property name (usually left blank if calling from property)</param>
		/// <returns>true if the value was set (false if its the same value)</returns>
		protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
		{
			if (Equals(storage, value))
			{
				return false;
			}

			storage = value;

			OnPropertyChanged(propertyName);

			return true;
		}

		/// <summary>
		/// Default implementation of Propertychanged event invoker
		/// </summary>
		/// <param name="propertyName">property that changed</param>
		protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
		{
			PropertyChangedEventArgs args = new PropertyChangedEventArgs(propertyName);
			List<GenericWeakAction<object, PropertyChangedEventArgs>> callList = null;

			lock (lockObject)
			{
				// Clean up
				for (int i = 0; i < propertychangeHandlers.Count;)
				{
					if (propertychangeHandlers[i].IsAlive)
					{
						i++;
					}
					else
					{
						propertychangeHandlers.RemoveAt(i);
					}
				}

				callList = new List<GenericWeakAction<object, PropertyChangedEventArgs>>(propertychangeHandlers);
			}

			foreach (GenericWeakAction<object, PropertyChangedEventArgs> propertychangeHandler in callList)
			{
				propertychangeHandler.Execute(this, args);
			}
		}
	}
}